
#pragma once

#include "variant_step.h"

#include <stack>

#if defined(__GNUC__) || defined(__MINGW32__) || defined(__MINGW__)
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Weffc++"
    #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
#endif

namespace tools{

    class Variant;

    namespace variant_detail{

//---------------------------------------------------------------------------------------------
        template<class T>struct IteratorBase: public VariantTypes
        {
            IteratorBase(){}

            bool Empty()const { return mCur.Empty() ||  *mCur == *mEnd; }

            bool operator==(const IteratorBase& rhs)const 
            {
//                 if(mCur.Empty() && mEnd.Empty() && rhs.mCur.Empty() )
//                     return true;
//                 if(*mCur == *mEnd && rhs.mCur.Empty() )
//                     return true;

                const size_t empty_this = Empty()? 1: 0;
                const size_t empty_other = rhs.Empty()? 1: 0;
                if(empty_this + empty_other == 2)
                    return true;
                if(empty_this != empty_other)
                    return false;


                return mCur==rhs.mCur;
            }
            bool operator!=(const IteratorBase& rhs)const{ return !operator==(rhs); }
            
            void operator=(const IteratorBase& rhs) { mCur = rhs.mCur; mEnd = rhs.mEnd; }

            const Str& GetKey()const{ return mCur->GetKey(); }
            const T& GetVariant()const   { return *Ptr(); }

            void Right() { mCur->Right(); }
            void Left()  { mCur->Left();  }

            const T& operator*()const  
            {
                const bool ERROR_DEREFERENCING_EMPTY_ITERATOR = Empty();
                assert(!ERROR_DEREFERENCING_EMPTY_ITERATOR);
                if(ERROR_DEREFERENCING_EMPTY_ITERATOR)
                    throw ::std::logic_error("ERROR_DEREFERENCING_EMPTY_ITERATOR");

                return *Ptr(); 
            }
            const T* operator->()const { return Empty()? nullptr: Ptr(); }

            T* Ptr()            { return static_cast<T*>(mCur->GetVariant());       }
            const T* Ptr()const { return static_cast<const T*>(mCur->GetVariant()); }
            Native mCur;
            Native mEnd;
        };
//---------------------------------------------------------------------------------------------


//---------------------------------------------------------------------------------------------
        template<class T> struct TIterator: IteratorBase<Variant>
        {
            typedef T My;
          
            TIterator(){}
            template<class U> TIterator(U& v)
            {
                if(!v.Empty())
                    Init(v);
            }

            bool operator==(const My& rhs)const{ return IteratorBase::operator==(rhs); }
            bool operator!=(const My& rhs)const{ return !operator==(rhs); }

            My& operator=(const My& rhs) { IteratorBase::operator=(rhs); return *this; }

            // преффиксная форма ++it
            My& operator++() { Right(); return static_cast<My&>(*this); }
            My& operator--() { Left();  return static_cast<My&>(*this); }

            // постфиксная форма it++
            My operator++(int) { const My i = *this; operator++(); return i; }
            My operator--(int) { const My i = *this; operator--(); return i; }
        private:
            template<class U> void Init( U& v )
            {
                mCur = v.mHelper->GetIterator( &v,true  );
                mEnd = v.mHelper->GetIterator( &v,false );
            }
            template<class U> void Init( const U& v )
            {
                mCur = v.mHelper->GetConstIterator( &v,true  );
                mEnd = v.mHelper->GetConstIterator( &v,false );
            }
        };
//---------------------------------------------------------------------------------------------
        struct Iterator: TIterator<Iterator> 
        {
            typedef TIterator<Iterator> Parent;
            Iterator(){}
            Iterator(Variant& v):Parent(v){}

            using IteratorBase::GetVariant;
            using IteratorBase::operator*;
            using IteratorBase::operator->;

            Variant& GetVariant() { return *Ptr(); }
            Variant& operator*()  { return *Ptr(); }
            Variant* operator->() { return Ptr();  }
        };
        struct ConstIterator: TIterator<ConstIterator> 
        {
            typedef TIterator<ConstIterator> Parent;
            ConstIterator(){}
            ConstIterator(const Variant& v):Parent(v){}
        };
//---------------------------------------------------------------------------------------------
        template<class>
        struct RecursiveIterator: public VariantTypes
        {
            #define My RecursiveIterator
            //typedef RecursiveIterator    My;
            typedef std::stack<Iterator> Stack;

            My(){}
            My(Variant& v)
            {
                mStack.emplace( Iterator(v) );
            }

            bool Empty()const{ return mStack.empty() || mStack.top().Empty(); }

            bool operator==(const My& rhs)const
            {
                const size_t empty_this = Empty()? 1: 0;
                const size_t empty_other = rhs.Empty()? 1: 0;
                if(empty_this + empty_other == 2)
                    return true;
                if(empty_this != empty_other)
                    return false;

                return mStack.top()==rhs.mStack.top();  
            }
            bool operator!=(const My& rhs)const{ return !operator==(rhs); }

            My& operator=(const My& rhs){ mStack = rhs.mStack; return *this; }

            // преффиксная форма ++it
            My& operator++()
            {
                CheckEmptyIterator();
                Variant& v = *Get();

                if(!v.IsPrimitive())
                {
                    mStack.emplace( Iterator(v) );
                    return *this;
                }

//                 if( v.Is<V::Vec>() )
//                     return mStack.emplace( CVStep(v) ), *this;
//                 else if( v.Is<V::Map>() )
//                     return mStack.emplace( CMStep(v) ), *this;

                Iterator& iter = mStack.top();

                ++iter;//->Increment();

                if( !iter.Empty() )
                    return *this;

//                 if( !mStack.top()->End())
//                     return *this;

                mStack.pop();
                while( mStack.size()!=0 && ( ++mStack.top(), mStack.top().Empty() ) )
                    mStack.pop();

                return  *this;
                
//                 Get().Right(); 
//                 return *this; 
            }
            My& operator--() { Get().Left();  return *this; }

            // постфиксная форма it++
            My operator++(int) { const My i = *this; operator++(); return i; }
            My operator--(int) { const My i = *this; operator--(); return i; }

            const Variant& operator*()const  { return *Get(); }
            const Variant* operator->()const { return Empty()? nullptr: Get().operator->(); }
            Variant& operator*()  { return *Get(); }
            Variant* operator->() { return Empty()? nullptr: Get().operator->(); }

            void CheckEmptyIterator()const
            {
                const bool ERROR_INCREMENT_OUT_RANGE = mStack.empty();
                assert(!ERROR_INCREMENT_OUT_RANGE);
                if(ERROR_INCREMENT_OUT_RANGE)
                    throw ::std::logic_error("ERROR_INCREMENT_OUT_RANGE");
            }

            Iterator& Get() { CheckEmptyIterator(); return mStack.top(); }
            const Iterator& Get()const { CheckEmptyIterator(); return mStack.top(); }

            Stack mStack;

            #undef My
        };

    }//namespace variant_detail
}//namespace tools

#if defined(__GNUC__) || defined(__MINGW32__) || defined(__MINGW__)
    #pragma GCC diagnostic pop
#endif
